﻿using System;
using System.Collections.Generic;
using System.Text;
using msilgc.CSyntaxTree;

namespace msilgc.cbuilder.CSyntaxTree
{
    public interface ICMethodBody
    {
        ICollection<IVariable> Arguments { get; }
        void EmitReturnType(StringBuilder builder);
        void EmitArguments(StringBuilder builder);
        void Emit(StringBuilder builder, IMethodNameMapper methodNameMapper);
        string MethodName { get; }
    }

    internal class CMethodBody : ICMethodBody {
        public bool InitLocals { get; set; }
        public ICollection<CBasicBlock> BasicBlocks { get; set; }
        public ICollection<IVariable> LocalVariables { get; set; }
        public ICollection<IVariable> Arguments { get; set; }
        public ICollection<IVariable> StackVariables { get; set; }
        public Type ReturnType { get; set; }
        public string MethodName { get; set; }
        
        public int ThreadIdxVariableOffset { get; set; }
        public int BlockIdxVariableOffset { get; set; }
        public int ThreadDimVariableOffset { get; set; }
        public int BlockDimVariableOffset { get; set; }

        public CMethodBody() {
            BasicBlocks = new List<CBasicBlock>();
            LocalVariables = new List<IVariable>();
            Arguments = new List<IVariable>();
            StackVariables = new List<IVariable>();
            ThreadIdxVariableOffset = (-1);
            BlockIdxVariableOffset = (-1);
            ThreadDimVariableOffset = (-1);
            BlockDimVariableOffset = (-1);
        }

        public void EmitReturnType(StringBuilder builder)
        {
            builder.Append(Variable.GetTypeString(ReturnType));
        }

        public void EmitArguments(StringBuilder builder)
        {
            bool first = true;
            foreach (Variable argument in Arguments) {
                if(argument == null) {
                    continue;
                }
                if (first) {
                    first = false;
                } else {
                    builder.Append(", ");
                }
                argument.EmitDeclaration(builder);
            }
        }

        public void Emit(StringBuilder builder, IMethodNameMapper methodNameMapper)
        {
            EmitReturnType(builder);  
            builder.Append(" ");
            builder.Append(MethodName);
            builder.Append("(");
            EmitArguments(builder);
            builder.AppendLine(") {");
            foreach (Variable localVariable in LocalVariables) {
                localVariable.EmitDeclaration(builder);
                if(InitLocals)
                {
                    if(localVariable.IsPrimitive)
                    {
                        builder.Append(" = 0");
                    }
                    else
                    {
                        throw new Exception("don't know how to init local of non primitive type");
                    }
                }
                builder.AppendLine(";");
            }
            builder.AppendLine();
            foreach (Variable stackVariable in StackVariables) {
                stackVariable.EmitDeclaration(builder);
                builder.AppendLine(";");
            }
            builder.AppendLine();
            foreach (CBasicBlock basicBlock in BasicBlocks)
            {
                basicBlock.Emit(builder, methodNameMapper);
            }
            builder.AppendLine("}");
        }
    }
}